/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.core.aop;

import java.lang.annotation.Annotation;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.Assert;

/**
 * 注释通知：{@link Advice 通知}植入到目标类连接点上的一段代码，就是增强什么地方？增强什么内容
 *
 * @author zeng
 */
public class AnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
  /**
   * 实现可以是任何类型的advice，例如拦截器。{@link org.aopalliance.intercept.Interceptor}
   *
   * <p>要执行的地方标识
   */
  private final Advice advice;

  /** 切入点 */
  private final Pointcut pointcut;

  /** 类型为注解的Class对象，限定必须是Annotation的子类型。 这个字段用于存储特定的注解类型，以便后续在代码中进行注解的查找和处理。 */
  private final Class<? extends Annotation> annotation;

  /**
   * 构造方法
   *
   * @param advice 切面
   * @param annotation 切入点注解
   */
  public AnnotationAdvisor(Advice advice, Class<? extends Annotation> annotation) {
    Assert.notNull(advice, "advice is not null!");
    Assert.notNull(annotation, "annotation is not null!");
    this.advice = advice;
    this.annotation = annotation;
    this.pointcut = this.buildPointcut();
  }

  @Override
  public Pointcut getPointcut() {
    return pointcut;
  }

  @Override
  public Advice getAdvice() {
    return advice;
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    if (this.advice instanceof BeanFactoryAware beanFactoryAware) {
      beanFactoryAware.setBeanFactory(beanFactory);
    }
  }

  private Pointcut buildPointcut() {
    Pointcut cpc = new AnnotationMatchingPointcut(this.annotation, true);
    Pointcut mpc = new AnnotationMethodPoint(this.annotation);
    return new ComposablePointcut(cpc).union(mpc);
  }
}
